#!/usr/bin/env python3
"""
PROFIBUS DP Slave implementation.

This script implements the PROFIBUS DP Slave role to communicate with
the PROFIBUS master over RS485. It handles slave parameterization
and I/O data exchange with the master.
In this implementation, we use pyprofibus's dummy slave capability for testing.
"""

import sys
import time
import logging
from pyprofibus import PbConf
from pyprofibus.gsd.interp import GsdInterp
from pyprofibus.dp_master import DpSlaveDesc

# Set up logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)


class ProfibusSlave:
    """
    PROFIBUS DP Slave class implementing the slave functionality.
    """
    
    def __init__(self, config_file="dp_slave.cfg", slave_addr=1):
        """
        Initialize the PROFIBUS DP Slave.
        
        Args:
            config_file: Path to the configuration file
            slave_addr: Slave address (0-127)
        """
        self.config_file = config_file
        self.slave_addr = slave_addr
        self.config = None
        
    def load_config(self):
        """
        Load the configuration from file.
        """
        logger.info(f"Loading configuration from {self.config_file}")
        
        # Load configuration from the specified config file
        try:
            with open(self.config_file, 'r') as fd:
                self.config = PbConf(fd, self.config_file)
            logger.info(f"Configuration loaded: {self.config.phyDev} at {self.config.phyBaud} baud")
        except FileNotFoundError:
            logger.error(f"Config file {self.config_file} not found!")
            raise
        
    def start_communication(self):
        """
        Start the PROFIBUS slave communication simulation.
        The slave just waits to be parameterized and communicated with by the master.
        """
        logger.info(f"Starting PROFIBUS slave simulation at address {self.slave_addr}...")
        
        try:
            # Create physical layer based on configuration
            phy = self.config.makePhy()
            logger.info(f"Physical layer created: {type(phy)}")
            
            # In the pyprofibus dummy slave implementation, the physical layer handles the simulation
            # We just need to keep the process alive so the dummy slave can respond to master requests
            logger.info("Slave ready to receive parameterization and I/O data from master...")
            
            # Keep the slave running - the physical layer handles the actual PROFIBUS protocol
            while True:
                time.sleep(0.1)  # Small delay to prevent excessive CPU usage
                logger.debug("Slave waiting for master communication...")
                
        except KeyboardInterrupt:
            logger.info("Slave communication interrupted by user")
        except Exception as e:
            logger.error(f"Slave communication error: {e}")
            
    def run(self):
        """
        Run the complete slave process.
        """
        try:
            # Load configuration
            self.load_config()
            
            # Start communication
            self.start_communication()
            
        except Exception as e:
            logger.error(f"Error in slave execution: {e}")
            raise


def main():
    """
    Main function to run the PROFIBUS slave.
    """
    print("Starting PROFIBUS DP Slave...")
    
    # Create slave instance
    # Uses the default dp_slave.cfg configuration file
    slave = ProfibusSlave(slave_addr=1)
    
    try:
        slave.run()
    except KeyboardInterrupt:
        print("\nSlave stopped by user.")
    except Exception as e:
        print(f"Error running slave: {e}")
        sys.exit(1)


if __name__ == "__main__":
    main()